import datetime
import json
import logging
import random
import re
import time
import uuid
from decimal import Decimal
from hashlib import md5

import pymongo
import xmltodict

from base.connection import mongodb_util, redis_util
from base.request import PCBaseHandler, require_logined
from common.definition import (OrderStatus, OrderType, PayStatus, PayType, ret_msg)
from conf import config
from conf.config import FACTORY_UID, GIFT_ORDER_REFUND_TIMEOUT
from helper.api_helper import ApiHelper
from helper.beanstalkd import add_mq_task
from helper.payment.balance import (BalanceHandler, create_balance_record,
                                    update_account_money)
from helper.payment.pay_util import (calculate_order_fee,
                                     calculate_pre_benefit, generator_trade_no)
from helper.payment.wechat_pay import WechatPay, weixin_verify
from helper.payment.weixin_xml import wechat_resp
from helper.user import AddressHelper, UserHelper
from lib.sms.SendTemplateSMS import ccp
from util.date_util import convert_ts_2_date
from util.json_util import JSONEncoder
from util.str_util import replace_emoji

logger = logging.getLogger("wechat.handlers.pc")


class QRConnectUrl(PCBaseHandler):
    """跳转到到扫码登陆页面"""
    async def get(self):
        url = self.client.qrconnect_url
        return self.redirect(url)


class PCWxLogin(PCBaseHandler):
    async def get(self):
        code = self.get_argument("code", None)
        state = self.get_argument("state", None)
        logger.info("code=%s, state=%s" % (code, state))
        if not code:
            url = self.client.qrconnect_url
            return self.redirect(url)
        res = self.client.fetch_access_token(code)
        logger.info("res=%s" % res)
        access_token = res['access_token']
        open_id = res['openid']
        user_info = self.client.get_user_info(open_id, access_token)
        nickname = replace_emoji(user_info["nickname"])
        sex = user_info["sex"]
        unionid = user_info.get("unionid", None)
        if not self.get_current_user():
            self.session.data = dict(
                open_id=open_id,
                nickname=nickname,
                sex=sex,
                unionid=unionid,
            )
            self.session.save()

            class User(object):
                pass

            cursor = self.get_db_session([
                {"db_table": "ebt_user", "model": User},
            ])
            user_info["openid"] = unionid
            user = cursor.query(User).filter_by(ebf_user_id=unionid).first()
            if not user:
                UserHelper.create_user(user_info, config.DEFAULT_AGENCY_UID)
        return self.redirect("https://www.style999.com/static/pc/mall/index.html")


class CheckLogin(PCBaseHandler):
    """检查登陆状态,data有数据表示已经登陆, data为空字典表示未登陆"""
    async def get(self):
        if self.get_current_user():
            return self.write(ret_msg(data=self.session.data, error_message="已登录"))
        else:
            return self.write(ret_msg(data=self.session.data, error_message="未登录"))


class PCRegister(PCBaseHandler):
    async def get(self):
        mobile = self.get_argument("mobile", None)
        if not mobile:
            return self.write(ret_msg(code=-1, error_message="手机号为空"))

        if not re.match(r"^1\d{10}$", mobile):
            return self.write(ret_msg(code=-1, error_message="手机号格式不正确"))

        sms_code = redis_util.client.get("sms_code_%s" % mobile)
        if sms_code:
            return self.write(ret_msg(code=-1, error_message="验证码已发送，如未收到请稍后重试"))

        sms_code = "%06d" % random.randint(0, 999999)
        try:
            redis = redis_util.client
            redis.setex("sms_code_%s" % mobile, config.SMS_CODE_EXPIRES_SECONDS, sms_code)
        except Exception as e:
            logging.error(e)
            raise Exception("save sms_code failed")

        try:
            result = ccp.sendTemplateSMS(mobile, [sms_code, int(config.SMS_CODE_EXPIRES_SECONDS / 60)], config.TEMP_ID)
            statusCode = result.get("statusCode")
            if statusCode != "000000":
                if statusCode == "160038":
                    return self.write(ret_msg(code=-1, error_message="获取短信验证码过于频繁"))
                elif statusCode == "160040":
                    return self.write(ret_msg(code=-1, error_message="获取短信验证码条数超出当天上限"))
                return self.write(ret_msg(code=-1, error_message="系统繁忙，请稍后重试！"))
        except Exception as e:
            logging.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙，请稍后重试！"))
        return self.write(ret_msg(data="OK"))

    async def post(self):
        mobile = self.json_args.get("mobile", "")
        user_sms_code = self.json_args.get("sms_code", "")
        pwd = self.json_args.get("passwd", "")
        repwd = self.json_args.get("repasswd", "")

        if not all((mobile, user_sms_code, pwd, repwd)):
            return self.write(ret_msg(code=-1, error_message="缺少参数"))

        if pwd != repwd:
            return self.write(ret_msg(code=-1, error_message="两次密码输入不一致"))

        if not re.match(r"^1\d{10}$", mobile):
            return self.write(ret_msg(code=-1, error_message="手机号格式不正确"))

        try:
            redis = redis_util.client
            sms_code = redis.get("sms_code_%s" % mobile)
        except Exception as e:
            logging.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙，请重试"))

        if not sms_code:
            return self.write(ret_msg(code=-1, error_message="验证码错误或已过期"))

        if user_sms_code != sms_code.decode("utf-8"):
            return self.write(ret_msg(code=-1, error_message="验证码错误"))

        class User(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_user", "model": User},
        ])

        user = session.query(User).filter_by(ebf_user_id="M" + mobile).first()
        if user:
            return self.write(ret_msg(code=-1, error_message="该手机号已注册"))
        md5_obj = md5()
        md5_obj.update(pwd.encode("utf-8"))
        password = md5_obj.hexdigest()
        try:
            new_user = User()
            new_user.ebf_user_id = "M" + mobile
            new_user.ebf_user_password = password
            new_user.ebf_user_create_date = convert_ts_2_date(
                str(time.time()).split(".")[0]) - datetime.timedelta(hours=8)
            new_user.ebf_user_phone = mobile
            new_user.ebf_user_factory_id = config.FACTORY_ID
            new_user.ebf_user_account = "M" + mobile
            new_user.ebf_user_phone = mobile
            session.add(new_user)
            session.commit()
        except Exception as e:
            logging.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="注册失败,请重试"))
        session.close()
        return self.write(ret_msg(data="OK"))


class PCPhoneLogin(PCBaseHandler):
    async def post(self):
        mobile = self.json_args.get("mobile", None)
        passwd = self.json_args.get("passwd", None)
        if not all((mobile, passwd)):
            return self.write(ret_msg(code=-1, error_message="手机号或者密码不能为空"))

        class User(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_user", "model": User},
        ])
        user = session.query(User).filter_by(ebf_user_id="M" + mobile).first()
        if not user:
            return self.write(ret_msg(code=-1, error_message="用户名不存在，请先注册再登录"))

        md5_obj = md5()
        md5_obj.update(passwd.encode("utf-8"))
        password = md5_obj.hexdigest()
        if password != user.ebf_user_password:
            return self.write(ret_msg(code=-1, error_message="密码错误"))
        if not self.get_current_user():
            self.session.data = dict(
                mobile="M" + mobile,
            )
            self.session.save()
        return self.write(ret_msg(data=mobile))


class PCLogout(PCBaseHandler):
    async def get(self):
        if self.get_current_user():
            self.session.clear()
        return self.write(ret_msg())


class Goods(PCBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()

        # 通用参数
        page_index = int(self.get_argument("page_index", 0))
        page_size = int(self.get_argument("page_size", 10))
        # 检查参数的合法性
        ApiHelper.inspect_common_params(page_index, page_size)

        page_start = page_index * page_size

        class Goods(object):
            pass

        class Carts(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_goods", "model": Goods},
            {"db_table": "sdet_cart", "model": Carts},
        ])

        filter = dict(
            sdef_goods_status=1,
            sdef_goods_is_delete=0,
            sdef_goods_factory_uid=config.FACTORY_UID,
        )
        goods_array = session.query(Goods).filter_by(**filter).order_by(
            Goods.sdef_goods_order_num.asc(),
            Goods.sdef_goods_price.asc())[page_start:page_start + page_size]
        cart_array = None
        if open_id:
            cart_array = session.query(Carts).filter_by(sdef_cart_device_id=open_id).all()

        cart_count = 0
        if cart_array:
            cart_goods = json.loads(cart_array[0].sdef_cart_goods)
            for g in cart_goods:
                cart_count = cart_count + g["count"]

        goods_list = []
        if len(goods_array) > 0:
            for g in goods_array:
                goods = {}
                goods["goods_id"] = g.sdef_goods_id
                goods["goods_thumbnail"] = json.loads(g.sdef_goods_thumbnail)
                goods["goods_name"] = g.sdef_goods_name
                goods["goods_price"] = str(g.sdef_goods_price.quantize(Decimal("0.00")))
                goods["goods_member_price"] = str(g.sdef_goods_member_price.quantize(Decimal("0.00")))
                goods["goods_count"] = g.sdef_goods_count
                goods["unit"] = ApiHelper.get_unit(g.sdef_goods_unit)
                goods_list.append(goods)

        is_membership = False
        if open_id:
            is_membership = self.is_membership(open_id)
        context = dict(
            goods_list=goods_list,
            cart_count=cart_count,
            is_membership=is_membership
        )
        return self.write(ret_msg(data=context))

    @require_logined
    async def post(self):
        open_id = await self.get_open_id()

        action = self.get_parameter("action", None)
        if action == "add-cart":
            goods_id = self.get_parameter("goods_id", None)
            count = self.get_parameter("count", None)
            if not count:
                count = 1
            count = int(count)

            if not (goods_id and re.match("^\d+$", goods_id)):
                return self.write(ret_msg(-1, error_message="商品编号错误"))

            class Carts(object):
                pass

            class Goods(object):
                pass

            session = self.get_db_session([
                {"db_table": "sdet_cart", "model": Carts},
                {"db_table": "sdet_goods", "model": Goods},
            ])

            filter = dict(
                sdef_goods_id=goods_id,
                sdef_goods_status=1,
                sdef_goods_is_delete=0
            )
            # 检查库存：必须是上架+未删除+库存大于0
            goods = session.query(Goods).filter(Goods.sdef_goods_count > 0).filter_by(**filter).first()
            if not goods:
                return self.write(ret_msg(-1, error_message="商品库存不足或已下架"))

            # 检查数量是否超出库存
            if count > goods.sdef_goods_count:
                return self.write(ret_msg(-1, error_message="商品库存不足"))

            cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id).first()
            cart_goods = []
            is_exists = False
            if cart:
                is_exists = True
                cart_goods = json.loads(cart.sdef_cart_goods)

                # 检查购物车是否已经有这个商品
                for c in cart_goods:
                    if c.get("id") == goods_id:
                        c["count"] += count
                        if c["count"] > goods.sdef_goods_count:
                            return self.write(ret_msg(-1, error_message="商品库存不足"))
                        # 修改
                        cart_goods_json = json.dumps(cart_goods)
                        session.query(Carts).filter_by(sdef_cart_device_id=open_id).update(
                            {Carts.sdef_cart_goods: cart_goods_json})
                        session.commit()
                        # 计算购物车商品数量
                        cart_count = 0
                        for c in cart_goods:
                            cart_count += c["count"]
                        return self.write(ret_msg(data=cart_count))

            # 新商品添加进购物车
            cart_goods.append({"id": goods_id, "count": count})
            cart_goods_json = json.dumps(cart_goods)
            if is_exists:
                # 修改
                session.query(Carts).filter_by(sdef_cart_device_id=open_id).update(
                    {Carts.sdef_cart_goods: cart_goods_json})
                session.commit()
            else:
                # 添加购物车
                _cart = Carts()
                _cart.sdef_cart_device_id = open_id
                _cart.sdef_cart_goods = cart_goods_json
                _cart.sdef_cart_update_time = datetime.datetime.now()
                session.add(_cart)
                session.commit()
        # 计算购物车商品数量
        cart_count = 0
        for c in cart_goods:
            cart_count += c["count"]
        return self.write(ret_msg(data=cart_count))


class PCPayConfirm(PCBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        # t=buy表示立刻购买
        t = self.get_argument("t", None)
        is_membership = self.is_membership(open_id)

        class Goods(object):
            pass

        class GoodsOrder(object):
            pass

        class Carts(object):
            pass

        class UserAddress(object):
            pass

        class AccountMoney(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_goods", "model": Goods},
            {"db_table": "sdet_cart", "model": Carts},
            {"db_table": "sdet_address", "model": UserAddress},
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "ebt_account_money", "model": AccountMoney},
        ])

        cart_goods_array = []
        cart_goods = []
        goods_list = []
        price_total = Decimal(0)
        balance_fee = Decimal(0)

        if t == "buy":
            goods_id = self.get_argument("id")
            count = self.get_argument("c")
            cart_goods.append({"id": goods_id, "count": count})

        elif t == "retry":
            # 继续支付
            old_order_id = self.get_argument("oid", None)
            old_order = session.query(GoodsOrder).filter_by(sdef_order_device_id=open_id,
                                                            sdef_order_id=old_order_id,
                                                            sdef_order_is_delete=0).first()
            if not old_order:
                return self.redirect("/404")

            if old_order.sdef_order_status != 1:
                return self.write(ret_msg(code=-1, error_message="只有处于待支付的订单才能继续支付"))

            # 继续支付时解除旧订单锁定的余额
            if old_order.sdef_order_balance_fee > Decimal("0"):
                redis = redis_util.client
                lock_balance_fee = redis.get("balance_lock_%s" % open_id)
                if lock_balance_fee:
                    lock_balance_fee = Decimal(lock_balance_fee.decode())
                    lock_balance_fee -= old_order.sdef_order_balance_fee
                    if lock_balance_fee > Decimal("0"):
                        redis.setex("balance_lock_%s" % open_id, 600, str(lock_balance_fee))
                    else:
                        redis.delete("balance_lock_%s" % open_id)
                    old_order.sdef_order_balance_fee = Decimal(0)
                    session.commit()

            cart_goods = json.loads(old_order.sdef_order_goods)

        # 再次购买
        elif t == "buy_again":
            old_order_id = self.get_argument("oid", None)
            old_order = session.query(GoodsOrder).filter_by(sdef_order_device_id=open_id,
                                                            sdef_order_id=old_order_id,
                                                            sdef_order_is_delete=0).first()
            if not old_order:
                return self.redirect("/404")

            if old_order.sdef_order_pay_status == 1 and old_order.sdef_order_type < 3:
                cart_goods = json.loads(old_order.sdef_order_goods)
            else:
                return self.write(ret_msg(code=-1, error_message="只有已经付款的普通订单或者礼品订单才能再次购买"))

        else:
            cart_array = session.query(Carts).filter_by(sdef_cart_device_id=open_id).all()
            if len(cart_array) > 0:
                cart_goods = json.loads(cart_array[0].sdef_cart_goods)

        for c in cart_goods:
            count = c["count"]
            goods_id = c["id"]
            # 必须是上架+未删除+库存大于0
            _goods = session.query(Goods).filter(Goods.sdef_goods_count > 0).filter_by(sdef_goods_id=goods_id,
                                                                                       sdef_goods_status=1,
                                                                                       sdef_goods_is_delete=0).first()
            if not _goods:
                return self.write(ret_msg(-1, error_message="商品库存不足或已下架"))

            goods_price = _goods.sdef_goods_member_price if is_membership else _goods.sdef_goods_price
            goods = dict(
                id=goods_id,
                count=count,
                balance_rate=_goods.sdef_goods_balance_rate,
                goods_img=json.loads(_goods.sdef_goods_thumbnail)[0],
                goods_name=_goods.sdef_goods_name,
                goods_price=str(goods_price.quantize(Decimal("0.00"))),
            )
            goods_dict = dict(
                id=goods_id,
                count=count
            )
            balance_fee += Decimal(_goods.sdef_goods_balance_rate) * Decimal(count) * goods_price
            price_total += goods_price * Decimal(count)
            cart_goods_array.append(goods)
            goods_list.append(goods_dict)

        uid = open_id
        default_address = AddressHelper.get_default(uid)
        use_address = AddressHelper.get_use(uid)
        db = mongodb_util.db
        address = None
        if use_address:
            logger.debug("使用地址，address_id=%s" % use_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=use_address["address_id"]).first()
        elif default_address:
            logger.debug("默认地址，address_id=%s" % default_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=default_address["address_id"]).first()
        else:
            result = db.sdec_address.find({"user_id": open_id, "is_delete": False}).sort("update_time",
                                                                                         pymongo.DESCENDING)
            for a in result:
                address = session.query(UserAddress).filter_by(sdef_address_id=a["address_id"]).first()
                break

        address_info = None
        if address:
            address_info = dict(
                province=address.sdef_address_province,
                city=address.sdef_address_city,
                county=address.sdef_address_county,
                detail=address.sdef_address_detail,
                name=address.sdef_address_name,
                phone=address.sdef_address_phone
            )
        # 获取账户余额
        enable_account_money = Decimal(0)
        account_money = session.query(AccountMoney).filter_by(ebf_am_user_id=open_id).first()
        if account_money:
            # 钱包余额需要减去redis缓存中暂扣的金额
            enable_account_money = account_money.ebf_am_money
            redis = redis_util.client
            lock_balance_fee = redis.get("balance_lock_%s" % open_id)
            if lock_balance_fee:
                enable_account_money = Decimal(enable_account_money) - Decimal(lock_balance_fee.decode())

            if account_money.ebf_am_is_forbid == 0 and enable_account_money > 0:
                if enable_account_money < balance_fee:
                    balance_fee = enable_account_money
            else:
                balance_fee = Decimal(0)
        else:
            balance_fee = Decimal(0)

        wechat_fee = price_total - balance_fee
        redis = redis_util.client
        # 页面唯一字符串，避免多次重复请求，重复支付
        unique_str = str(uuid.uuid1())
        redis.setex("unique_string_%s" % unique_str, 600, 1)
        context = dict(
            cart_goods_array=cart_goods_array,
            price_total=str(price_total.quantize(Decimal("0.00"))),
            wechat_fee=str(wechat_fee.quantize(Decimal("0.00"))),
            balance_fee=str(balance_fee.quantize(Decimal("0.00"))),  # 抵扣的余额
            address=address_info,
            enable_account_money=str(enable_account_money.quantize(Decimal("0.00"))),  # 可用余额
            unique_str=unique_str,
            goods_list=goods_list,
            t=t
        )
        return self.write(ret_msg(data=context))

    async def post(self):
        open_id = await self.get_open_id()
        is_membership = self.is_membership(open_id)
        pay_type = int(self.get_parameter("pay_type", PayType.WECHAT))
        balance_fee = Decimal(self.get_parameter("balance_fee"))

        # 订单类型（1=普通订单，2=送礼订单， 3=加盟费订单， 4=特惠订单）
        order_type = int(self.get_parameter("order_type", 1))
        enable_balance_fee = Decimal("0")
        message = self.get_parameter("message", "")
        if pay_type not in [PayType.WECHAT, PayType.BALANCE]:
            return self.write(ret_msg(code=-1, error_message="目前只支持微信支付和余额支付"))

        unique_str = self.get_parameter("unique_str")
        redis = redis_util.client
        unique_string = redis.get("unique_string_%s" % unique_str)
        if not unique_string:
            return self.write(ret_msg(code=-1, error_message="页面已失效，请刷新页面"))
        else:
            redis.delete("unique_string_%s" % unique_str)

        # 是否使用余额抵扣, 默认使用
        is_use_balance = self.get_parameter("is_use_balance", "true")
        if is_use_balance != "true":
            balance_fee = Decimal("0")

        t = self.get_parameter("t", None)
        ip = self.request.remote_ip

        class UserAddress(object):
            pass

        class GoodsOrder(object):
            pass

        class Goods(object):
            pass

        class Carts(object):
            pass

        class User(object):
            pass

        class AccountMoney(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_goods", "model": Goods},
            {"db_table": "sdet_cart", "model": Carts},
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "sdet_address", "model": UserAddress},
            {"db_table": "ebt_user", "model": User},
            {"db_table": "ebt_account_money", "model": AccountMoney},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency},
        ])
        # 创建订单
        uid = open_id
        default_address = AddressHelper.get_default(uid)
        use_address = AddressHelper.get_use(uid)
        db = mongodb_util.db
        address = None
        if use_address:
            logger.debug("使用地址，address_id=%s" % use_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=use_address["address_id"]).first()
        elif default_address:
            logger.debug("默认地址，address_id=%s" % default_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=default_address["address_id"]).first()
        else:
            result = db.sdec_address.find({"user_id": open_id, "is_delete": False}).sort("update_time",
                                                                                         pymongo.DESCENDING)
            for a in result:
                address = session.query(UserAddress).filter_by(sdef_address_id=a["address_id"]).first()
                break

        # 送礼订单允许地址为空
        if not address and order_type != OrderType.GIFT:
            return self.write(ret_msg(code=-1, error_message="请先完善收货地址"))

        cart_goods = json.loads(self.get_parameter("goods_list"))
        cart_goods_array = []
        price_total = Decimal("0")

        for c in cart_goods:
            filter = dict(
                sdef_goods_id=c["id"],
                sdef_goods_status=1,
                sdef_goods_is_delete=0
            )
            _goods = session.query(Goods).filter_by(**filter).first()
            if not _goods:
                return self.write(ret_msg(-1, error_message="商品库存不足或已下架"))

            count = int(c["count"])
            goods_id = str(c["id"])
            goods_img = json.loads(_goods.sdef_goods_thumbnail)[0]
            goods_price = _goods.sdef_goods_member_price if is_membership else _goods.sdef_goods_price

            # 检查商品是否限购
            if _goods.sdef_goods_limit_num != 0:
                if _goods.sdef_goods_limit_num <= count:
                    continue

            # 下单时减少库存
            if _goods.sdef_goods_count >= count:
                _goods.sdef_goods_count -= count
            else:
                continue

            goods = dict(
                id=goods_id,
                count=count,
                goods_img=goods_img,
                goods_name=_goods.sdef_goods_name,
                goods_price=goods_price,
                goods_member_price=_goods.sdef_goods_member_price,
                goods_pin_price=_goods.sdef_goods_pin_price,
                balance_rate=_goods.sdef_goods_balance_rate,
                rate=_goods.sdef_goods_rate,
                master_rate=_goods.sdef_goods_master_rate,
                invite_rate=_goods.sdef_goods_invite_rate,
                invite_invite_rate=_goods.sdef_goods_invite_invite_rate,
                career_rate=_goods.sdef_goods_career_rate,
                invite_career_rate=_goods.sdef_goods_invite_career_rate,
                supplier=_goods.sdef_goods_supplier,
                goods_cost=_goods.sdef_goods_cost,
            )
            enable_balance_fee += Decimal(_goods.sdef_goods_balance_rate) * Decimal(count) * goods_price
            price_total += goods_price * Decimal(count)
            cart_goods_array.append(goods)

        # 获取账户余额
        account_money = session.query(AccountMoney).filter_by(ebf_am_user_id=open_id).first()
        lock_balance_fee = redis.get("balance_lock_%s" % open_id)
        if account_money:
            # 钱包余额需要减去redis缓存中暂扣的金额
            enable_account_money = account_money.ebf_am_money

            if lock_balance_fee:
                enable_account_money = Decimal(enable_account_money) - Decimal(lock_balance_fee.decode())

            if account_money and account_money.ebf_am_is_forbid == 0 and enable_account_money > 0:
                if enable_account_money < enable_balance_fee:
                    enable_balance_fee = enable_account_money
            else:
                enable_balance_fee = Decimal("0")
        else:
            enable_balance_fee = Decimal("0")

        if enable_balance_fee < balance_fee:
            return self.write(ret_msg(code=-1, error_message="可抵扣余额错误"))

        wechat_fee = price_total - balance_fee
        logger.info("price_total=%s,balance_fee=%s,wechat_fee=%s" % (price_total, balance_fee, wechat_fee))

        if wechat_fee < Decimal("0.01"):
            pay_type = 4  # 支付方式直接标记为余额支付

        now = datetime.datetime.now()
        goods_order = GoodsOrder()
        out_trade_no = generator_trade_no()
        goods_order.sdef_order_id = out_trade_no
        goods_order.sdef_order_factory_uid = FACTORY_UID
        goods_order.sdef_order_device_id = open_id  # 微信用户openId
        goods_order.sdef_order_fee = wechat_fee
        # goods_order.sdef_order_fee = Decimal("0.01")  # 测试一分钱付款
        goods_order.sdef_order_goods = json.dumps(cart_goods_array, cls=JSONEncoder)
        goods_order.sdef_order_balance_fee = balance_fee
        goods_order.sdef_order_name = "思达尔商城订单"
        goods_order.sdef_order_message = message
        goods_order.sdef_order_describe = "商城订单"

        if pay_type == PayType.WECHAT:
            goods_order.sdef_order_status = OrderStatus.WAIT_PAY
            goods_order.sdef_order_pay_status = PayStatus.NOT_PAY
        else:
            goods_order.sdef_order_status = OrderStatus.WAIT_EXPRESS
            goods_order.sdef_order_pay_status = PayStatus.SUCCESS
            goods_order.sdef_order_pay_time = now

        # 送礼的订单不需要地址
        goods_order.sdef_order_pay_type = pay_type
        if order_type == OrderType.GIFT:
            goods_order.sdef_order_type = OrderType.GIFT
        else:
            goods_order.sdef_order_type = OrderType.NORMAL
            goods_order.sdef_order_address_id = address.sdef_address_id

        goods_order.sdef_order_ip = ip
        goods_order.sdef_order_delivery_fee = 0  # 邮费
        goods_order.sdef_order_create_time = now
        # 获取当前用户所属的合伙人填入订单中（没有合伙人的使用默认合伙人的uid）
        agency_uid = config.DEFAULT_AGENCY_UID
        users = session.query(User).filter_by(ebf_user_id=open_id).all()
        if len(users) == 1:
            user = users[0]
            if user.ebf_user_agency_uid:
                agency_uid = user.ebf_user_agency_uid
        goods_order.sdef_order_agency_uid = agency_uid
        session.add(goods_order)

        redis = redis_util.client
        redis.setex("order_id_%s" % out_trade_no, 86400, out_trade_no)

        if t == "buy":
            pass
        elif t == "retry":
            old_order_id = self.get_parameter("oid", None)
            old_order = session.query(GoodsOrder).filter_by(sdef_order_device_id=open_id,
                                                            sdef_order_id=old_order_id,
                                                            sdef_order_is_delete=0).all()[0]
            if old_order.sdef_order_status != OrderStatus.WAIT_PAY:
                return self.write(ret_msg(code=-1, error_message="只有处于待支付的订单才能继续支付"))
            # 获取之前的支付信息，创建完新订单之后将之前的订单标记为删除
            old_order.sdef_order_is_delete = 1
            old_order.sdef_order_delete_time = datetime.datetime.now()
            logger.debug("将旧订单标记为删除：%s" % old_order_id)
        elif t == "buy_again":
            pass
        else:
            ret = session.query(Carts).filter_by(sdef_cart_device_id=open_id).delete()
            logger.info("delete cart ret=%s" % ret)

        if pay_type == PayType.BALANCE:
            logger.info("余额支付")
            account_money = session.query(AccountMoney).filter_by(ebf_am_user_id=open_id).first()
            if not account_money:
                return self.write(ret_msg(code=-1, error_message="账户未开通"))

            if account_money.ebf_am_is_forbid != 0:
                return self.write(ret_msg(code=-1, error_message="账户不可用"))

            if account_money.ebf_am_money < price_total:
                return self.write(ret_msg(code=-1, error_message="账户余额不足"))
            # 计算提成金额
            price_total_rate = calculate_order_fee(goods_order.sdef_order_goods)[0]
            # 扣款并添加扣款记录，同时往ebt_pay表中添加一条订单
            if not BalanceHandler.reduce_money(open_id, out_trade_no, price_total, price_total_rate, ip):
                return self.write(ret_msg(code=-1, error_message="扣款失败"))

            is_success = create_balance_record(open_id, price_total, out_trade_no)
            if is_success:
                logger.info("创建余额消费记录成功:user_id=%s,out_trade_no=%s" % (open_id, out_trade_no))
            else:
                logger.error("创建余额消费记录失败:user_id=%s,out_trade_no=%s" % (open_id, out_trade_no))

            context = dict(
                trade_no=out_trade_no,
            )
            session.commit()
            # 生成预收益订单
            # calculate_pre_benefit(out_trade_no)
            return self.write(ret_msg(data=context))

        try:
            session.commit()
        except Exception as e:
            logger.exception(e)
            session.rollback()
            return self.finish(ret_msg(code=-1, error_message="订单创建异常"))

        nonce_str = WechatPay.generate_nonce_str()
        # 生成预支付交易单
        logger.info("生成预支付交易单")

        try:
            # 用一分钱测试pc付款
            # wechat_fee = 0.01
            code_url = WechatPay.pre_order(out_trade_no, int(wechat_fee * 100), "商城订单", ip, nonce_str)
            logger.info("code_url=%s, order_id=%s" % (code_url, out_trade_no))
            # 将暂扣的余额存入redis中，超时时间10分钟，微信支付回调时直接扣余额即可
            if lock_balance_fee:
                lock_balance_fee = Decimal(lock_balance_fee.decode()) + balance_fee
            else:
                lock_balance_fee = balance_fee
            redis.setex("balance_lock_%s" % open_id, 600, str(lock_balance_fee))

            if use_address:
                if default_address:
                    if use_address["address_id"] != default_address["address_id"]:
                        is_default = True
                        is_use = True
                        AddressHelper.set_default(open_id, default_address["address_id"], is_default, is_use)
                else:
                    is_default = False
                    is_use = False
                    AddressHelper.set_default(open_id, use_address["address_id"], is_default, is_use)

            # 微信支付的加入消息队列，6分钟后检查是否已支付
            add_mq_task(out_trade_no, message_type=1, delay=360)
            # 如果是礼品订单，则在48小时后检查是否需要退款
            if order_type == OrderType.GIFT:
                add_mq_task(out_trade_no, message_type=2, delay=GIFT_ORDER_REFUND_TIMEOUT)

            if code_url:
                context = dict(
                    code_url=code_url,
                    trade_no=out_trade_no
                )
                return self.write(ret_msg(data=context))
            else:
                return self.write(ret_msg(code=-1, error_message="二维码失效"))
        except Exception as e:
            logger.exception(e)
            return self.write(ret_msg(code=-1, error_message="支付异常"))


class PCPayNotify(PCBaseHandler):
    async def post(self):
        logger.info("wechat_async_notify")

        pay_info = self.request.body.decode("utf8").replace('\n', '').replace(' ', '')
        if not pay_info:
            return self.write(wechat_resp(40001))

        logger.info(pay_info)
        params = xmltodict.parse(pay_info, encoding='utf-8').get('xml', None)
        logger.info("xml解析成功")
        # 参数传入错误
        if not params:
            ret = wechat_resp(40001)
            logger.info(ret)
            return self.write(ret)

        # 验证传输码
        return_code = params.get('return_code', None)
        if not return_code or return_code != 'SUCCESS':
            ret = wechat_resp(46011)
            logger.info(ret)
            return self.write(ret)

        # 验证交易码
        result_code = params.get('result_code', None)
        if not result_code or result_code != 'SUCCESS':
            ret = wechat_resp(46012)
            logger.info(ret)
            return self.write(ret)

        # 验证微信消息
        if not weixin_verify(params):
            ret = wechat_resp(46005)
            logger.info(ret)
            return self.write(ret)

        # 验证订单号
        out_trade_no = params.get('out_trade_no', None)
        if not out_trade_no:
            ret = wechat_resp(46004)
            logger.info(ret)
            return self.write(ret)

        class GoodsOrder(object):
            pass

        class User(object):
            pass

        class Pay(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        class Benefit(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "ebt_user", "model": User},
            {"db_table": "ebt_pay", "model": Pay},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency},
            {"db_table": "sdet_benefit", "model": Benefit},
        ])
        _order_array = session.query(GoodsOrder).filter_by(sdef_order_id=out_trade_no).all()
        if len(_order_array) == 0:
            ret = wechat_resp(46002)
            logger.info(ret)
            return self.write(ret)
        if len(_order_array) > 1:
            # 订单多条记录异常
            ret = wechat_resp(46003)
            logger.info(ret)
            return self.write(ret)

        pay = _order_array[0]

        # 如果订单记录的支付状态不是0（未完成）， 则直接返回即可，不需要继续处理
        if pay.sdef_order_pay_status != PayStatus.NOT_PAY:
            return self.write(wechat_resp(0))

        # 验证支付费用是否一致
        pay_fee = pay.sdef_order_fee
        total_fee = params.get('total_fee', None)
        logging.debug("pay_fee:%s,total_fee:%s" % (pay_fee, total_fee))
        if (not pay_fee) or (not total_fee) or (("%.2f" % (float(pay_fee) * 100)) != ("%.2f" % float(total_fee))):
            ret = wechat_resp(46001)
            logger.info(ret)
            return self.write(ret)

        # 更新订单为已支付
        logger.info("out_trade_no=%s" % out_trade_no)
        # 订单状态（0 = 未知，1 = 待支付，2 = 待发货，3 = 已发货，4 = 完成, 5 = 失效）
        pay.sdef_order_status = OrderStatus.WAIT_EXPRESS
        # 支付状态（0 = 未支付，1 = 支付成功，2 = 退款）
        pay.sdef_order_pay_status = PayStatus.SUCCESS
        pay.sdef_order_pay_time = datetime.datetime.now()
        session.commit()
        user_id = pay.sdef_order_device_id

        # 扣除锁定的余额
        if pay.sdef_order_balance_fee > Decimal("0"):
            redis = redis_util.client
            is_reduce = redis.get("reduce_balance_%s" % out_trade_no)
            if not is_reduce:
                redis.setex("reduce_balance_%s" % out_trade_no, 86400, str(pay.sdef_order_balance_fee))
                is_success = update_account_money(user_id, -pay.sdef_order_balance_fee)
                if not is_success:
                    raise Exception("扣款异常:user_id=%s,out_trade_no=%s" % (user_id, out_trade_no))
                else:
                    logger.info("订单余额抵扣成功：order_id=%s" % out_trade_no)
                    resulte = create_balance_record(user_id, pay.sdef_order_balance_fee, out_trade_no)
                    if resulte:
                        logger.info("创建余额消费记录成功:user_id=%s,out_trade_no=%s" % (user_id, out_trade_no))
                    else:
                        logger.error("创建余额消费记录失败:user_id=%s,out_trade_no=%s" % (user_id, out_trade_no))

        # 提交
        session.commit()
        # calculate_pre_benefit(out_trade_no)
        logger.info("支付回调处理成功")
        return self.write(wechat_resp(0))


class OrderQuery(PCBaseHandler):
    """调用微信查询订单接口查询订单是否已付款实现前端页面跳转"""
    async def get(self):
        order_id = self.get_argument("order_id", "")
        if not order_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数:订单编号"))

        class GoodsOrder(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
        ])
        o = session.query(GoodsOrder).filter_by(sdef_order_id=order_id).first()
        if not o:
            logger.error("order_query 订单不存在：order_id=%s" % order_id)
            return self.write(ret_msg(code=-1, error_message="订单不存在"))
        nonce_str = WechatPay.generate_nonce_str()
        order = WechatPay.mp_order_query(order_id, nonce_str)
        if not order:
            logger.error("order_query 交易不存在：order_id=%s" % order_id)
            return self.write(ret_msg(code=-1, error_message="微信服务器没有该订单支付记录"))
        logger.info(order)
        if order.get("trade_state") != "SUCCESS":
            logger.error("order_query 支付未成功：order_id=%s" % order_id)
            return self.write(ret_msg(code=-1, error_message="支付未成功"))

        if o.sdef_order_pay_status != PayStatus.NOT_PAY:
            logger.error("order_query 订单已处理：order_id=%s" % order_id)
        else:
            logger.info("order_query 更新订单表:%s" % order)
            third_trade_no = order.get("transaction_id")
            o.sdef_order_pay_status = PayStatus.SUCCESS
            o.sdef_order_status = OrderStatus.WAIT_EXPRESS
            o.sdef_order_pay_time = datetime.datetime.now()
            o.sdef_order_trade_no = third_trade_no
            session.commit()

            # 如果订单是微信和余额混合支付, 扣除锁定的余额
            if o.sdef_order_balance_fee > Decimal("0"):
                user_id = o.sdef_order_device_id
                redis = redis_util.client
                is_reduce = redis.get("reduce_balance_%s" % order_id)
                if not is_reduce:
                    redis.setex("reduce_balance_%s" % order_id, 86400, str(o.sdef_order_balance_fee))
                    is_success = update_account_money(order_id, -o.sdef_order_balance_fee)
                    if not is_success:
                        logger.error("扣款异常:user_id=%s,out_trade_no=%s" % (user_id, order_id))
                    else:
                        logger.info("订单余额抵扣成功：order_id=%s" % order_id)
                        resulte = create_balance_record(user_id, o.sdef_order_balance_fee, order_id)
                        if resulte:
                            logger.info("创建余额消费记录成功:user_id=%s,out_trade_no=%s" % (user_id, order_id))
                        else:
                            logger.error("创建余额消费记录失败:user_id=%s,out_trade_no=%s" % (user_id, order_id))
        return self.write(ret_msg(data=0, error_message="支付成功"))
